/*
*	This is created in 2011.4.5
*	First Edit at 2011.4.6
*	Finished at 2011.5.1
*	With Memory Access Delay and Persudo Dynamic Branch Prediction
*
*/

`include "definitions.v"
`include "ALU.v"
`include "Conflict_Deal.v"
`include "Controller.v"
`include "Ext.v"
`include "Reg_F.v"
`include "Reg_DE.v"
`include "Reg_EM.v"
`include "Reg_FD.v"
`include "Reg_MW.v"
`include "Reg_Files.v"
`include "Memory.v"
`include "PredictionReservation.v"
`include "HaltChecker.v"

module CPU;
/*
* Declaration for wires in CPU
*/

// File Monitor Handler
	integer handle; 

// Clock Reg
	reg Clock;

// wires in Fetch Part
	wire[31:0] PC,PCF,PCJ,PCPlus4F,InstrF,JumpAddr,SMCAddr,PCReal,PCBase;
	
// wires in Decode Part
	wire[31:0] InstrFD,RealAddrD;
	wire[31:0] InstrD,PCPlus4D,SignImmD,ShamtD,RD1D,RD2D,RD1Dtemp,RD2Dtemp,PCBranchD,JImmD;
	wire[4:0] RsD,RtD,RdD;
	wire[2:0] ALUControlD;
	wire[1:0] ALUSrcD,ExtOp;
	wire[5:0] Op,Funct;
	
	wire RegWriteD,MemtoRegD,MemWriteD,RegDstD,BranchD,BrEqD,PCSrc,JumpRegD,JumpRunD,JumpD,FlushD;
	
	
// wires in Execute Part
	wire[31:0] RD1E,RD2E,ShamtE,SignImmE,SrcAE,SrcBE,WriteDataE,ALUOutE,PCPlus4E;
	wire[4:0] RsE,RtE,RdE,WriteRegE;
	wire[2:0] ALUControlE;
	wire[1:0] ALUSrcE;
	wire RegWriteE,MemtoRegE,MemWriteE,RegDstE;
	
// wires in Memory Part
	wire[31:0] ALUOutM,WriteDataM,ReadDataM;
	wire[4:0] WriteRegM;
	wire RegWriteM,MemtoRegM,MemWriteM;
	wire StallAll,InstrReady,DataReady;

// wires in WriteBack Part
	wire[31:0] ReadDataW,ALUOutW,ResultW;
	wire[4:0] WriteRegW;
	wire RegWriteW,MemtoRegW;

// wires in Conflict Detect
	wire StallF,StallD,ForwardAD,ForwardBD,FlushE,SMCOccurLev1,SMCOccurLev2;
	wire[1:0] ForwardAE,ForwardBE;

// wire in Advance Modules
	wire DoHalt,ChangeState;
	wire DoPredict;
	wire[31:0] PredictInstr,GoOnAddr;
	integer i;
/*
* Bind Wires
*/

//For Fetch Part
	assign PCBase = SMCOccurLev2 ? SMCAddr : PCPlus4F;
	assign PC = PCSrc ? (DoPredict == 1? GoOnAddr : PCBranchD): PCBase;
	assign PCJ = JumpD ?(DoPredict == 1? GoOnAddr : JumpAddr) : PC;
	assign PCReal = SMCOccurLev1 ? SMCAddr : PCJ;
	assign PCPlus4F = PCF + 4;
	assign JumpAddr = JumpRegD ? RD1D : JImmD;
	Reg_F myReg_F(PCReal,~StallF & ~StallAll,Clock,PCF); 	
	// lack for memory bind
	
//For Decode Part
	assign InstrFD = DoPredict == 1 ? PredictInstr:InstrF;
	assign RealAddrD = DoPredict == 1 ? GoOnAddr : PCPlus4F;
	Reg_FD myReg_FD(InstrFD,InstrD,RealAddrD,PCPlus4D,FlushD,Clock,~StallD & ~StallAll);
	assign Op = InstrD[31:26];
	assign Funct = InstrD[5:0];
	assign RsD = InstrD[25:21];
	assign RtD = InstrD[20:16];
	assign RdD = InstrD[15:11];
	assign ShamtD = {27'b000000000000000000000000000,InstrD[10:6]};
	Ext myExt(InstrD[15:0],ExtOp,SignImmD);
	assign PCBranchD = (SignImmD << 2) + PCPlus4D;
	
	Reg_Files myReg_Files(Clock,RsD,RtD,WriteRegW,ResultW,RegWriteW,RD1Dtemp,RD2Dtemp);
	assign RD1D = ForwardAD ? ALUOutM : RD1Dtemp;
	assign RD2D = ForwardBD ? ALUOutM : RD2Dtemp;
	assign PCSrc = (BranchD & ((((RD1D == RD2D) && (BrEqD == 1)) || ((RD1D != RD2D) && (BrEqD == 0))))? 1 : 0);
	
	Controller myController(Op,Funct,RegWriteD,MemtoRegD,MemWriteD,ALUControlD,ALUSrcD,ExtOp,RegDstD,BranchD,BrEqD,
							JumpD,JumpRunD,JumpRegD);
	
	assign FlushD = (JumpD & !DoPredict) | (PCSrc & !DoPredict) | SMCOccurLev1 | SMCOccurLev2;
	assign JImmD = {PCPlus4F[31:28],InstrD[25:0],2'b00};
	assign JumpAddr = JumpRegD ? RD1D : JImmD;
	
//For Execute Part
	Reg_DE myReg_DE(RegWriteD,MemtoRegD,MemWriteD,ALUControlD,ALUSrcD,RegDstD,
					RsD,RtD,RdD,ShamtD,SignImmD,RD1D,RD2D,
					RegWriteE,MemtoRegE,MemWriteE,ALUControlE,ALUSrcE,RegDstE,
					RsE,RtE,RdE,ShamtE,SignImmE,RD1E,RD2E,
					FlushE,Clock,PCPlus4D,PCPlus4E,JumpRunD,~StallAll);
	assign SrcAE = (ForwardAE == 2'b00) ? RD1E :
					((ForwardAE == 2'b01) ? ResultW :
					ALUOutM);//as for 10
	assign WriteDataE = (ForwardBE == 2'b00) ? RD2E :
					((ForwardBE == 2'b01) ? ResultW :
					ALUOutM);//as for 10
	assign SrcBE = (ALUSrcE == 2'b00) ? WriteDataE:
					((ALUSrcE == 2'b01) ? ShamtE:
					SignImmE);//as for 10
	assign WriteRegE = RegDstE ? RdE : RtE;
	ALU myALU(SrcAE,SrcBE,ALUControlE,ALUOutE);
	
//For Memory Part
	Reg_EM myReg_EM(RegWriteE,MemtoRegE,MemWriteE,ALUOutE,WriteDataE,WriteRegE,
					RegWriteM,MemtoRegM,MemWriteM,ALUOutM,WriteDataM,WriteRegM,
					Clock,~StallAll);
	// lack for memory bind

//For WriteBack Part
	Reg_MW myReg_MW(RegWriteM,MemtoRegM,ReadDataM,ALUOutM,WriteRegM,
					RegWriteW,MemtoRegW,ReadDataW,ALUOutW,WriteRegW,
					Clock,~StallAll);
	assign ResultW = MemtoRegW ? ReadDataW : ALUOutW;
	
//For Conflict_Deal Part
	Conflict_Deal myConflict_Deal(RsD,RtD,RsE,RtE,WriteRegE,WriteRegM,WriteRegW,BranchD,MemtoRegE,MemtoRegM,
									RegWriteE,RegWriteM,RegWriteW,
									StallF,StallD,ForwardAD,ForwardBD,ForwardAE,ForwardBE,FlushE,
									SMCOccurLev1,SMCOccurLev2,SMCAddr,PCPlus4E,ALUOutE,MemWriteE);
	
/*
*	Memory Access Module bind;
*	In Mid Check, I push memory into CPU;
* 
*/ 
	assign StallAll = (MemtoRegM & !DataReady) | !InstrReady; 
	Memory myMemory(PCF,ALUOutM,WriteDataM,MemWriteM,InstrF,ReadDataM,Clock,MemtoRegM,DataReady,InstrReady);
	
/*
*	For Persudo Dynamic Branch Prediction
*
*/

	PredictionReservation myPR(InstrD,PCPlus4D,PCSrc,JumpD,PCBranchD,JumpAddr,ALUOutE,WriteDataE,MemWriteE,PredictInstr,GoOnAddr,DoPredict);
	
/*
*	For HaltChecker
*/
	assign ChangeState = RegWriteE | MemWriteE | RegWriteM | MemWriteM | RegWriteW;
	HaltChecker myHaltChecker(InstrD,ChangeState,DoHalt);
	always @(DoHalt) begin
		if(DoHalt == 1) begin
			for(i = 0 ; i < 32 ; i = i+1)
				$fdisplay(handle,"%b",myReg_Files.data[i]);
			for(i = 0 ; i < 10 ; i = i+1)
				$fdisplay(handle,"%b",{myMemory.data[4*i],myMemory.data[4*i+1],myMemory.data[4*i+2],myMemory.data[4*i+3]});
			for(i = 0 ; i < 10 ; i = i+1)
				$fdisplay(handle,"%b",{myMemory.data[(1<<19)+4*i],myMemory.data[(1<<19)+4*i+1],myMemory.data[(1<<19)+4*i+2],myMemory.data[(1<<19)+4*i+3]});
			$finish;
		end
	end
	
	//Define a Clock and test
	
	initial begin
		Clock <= 0;
		handle = $fopen("test.out");
		$readmemb("test.mif",myMemory.data);
	end
	
	
	
	always @(Clock) begin
	/*	for Dynamic Branch Prediction Test
		$fdisplay(handle,"time = %0d,instrD:%b",$time,InstrD);
		$fdisplay(handle,"time = %0d,PCPlus4D = %0d",$time,PCPlus4D);
		$fdisplay(handle,"time = %0d,DoPredict = %b",$time,DoPredict);
		$fdisplay(handle,"time = %0d,PredictInstr = %0d",$time,PredictInstr);
		$fdisplay(handle,"time = %0d,GoOnAddr = %0d",$time,GoOnAddr);
		$fdisplay(handle,"time = %0d,FlushD = %0d",$time,FlushD);
		$fdisplay(handle,"time = %0d,InstrFD = %b",$time,InstrFD);
	/*/
	/* for F
		$fdisplay(handle,"time = %0d,clock = %b",$time,Clock);
		$fdisplay(handle,"time = %0d,instrF:%b",$time,InstrF);
		$fdisplay(handle,"time = %0d,PCF:%0d",$time,PCF);
		$fdisplay(handle,"time = %0d,PCPlus4F:%0d",$time,PCPlus4F);
		$fdisplay(handle,"time = %0d,PC:%0d",$time,PC);
		$fdisplay(handle,"time = %0d,PCJ:%0d",$time,PCJ);
	/*/
	
	/* for D
		$fdisplay(handle,"time = %0d,instrD:%b",$time,InstrD);
		$fdisplay(handle,"time = %0d,RsD = %0d",$time,RsD);
		$fdisplay(handle,"time = %0d,RtD = %0d",$time,RtD);
		$fdisplay(handle,"time = %0d,RdD = %0d",$time,RdD);
		$fdisplay(handle,"time = %0d,RD1Dtemp = %0d",$time,RD1Dtemp);
		$fdisplay(handle,"time = %0d,RD2Dtemp = %0d",$time,RD2Dtemp);
		$fdisplay(handle,"time = %0d,RD1D = %0d",$time,RD1D);
		$fdisplay(handle,"time = %0d,RD2D = %0d",$time,RD2D);
		$fdisplay(handle,"time = %0d,ExtOp = %b",$time,ExtOp);
		$fdisplay(handle,"time = %0d,ALUControlD = %b",$time,ALUControlD);
		$fdisplay(handle,"time = %0d,ALUSrcD = %b",$time,ALUSrcD);
		$fdisplay(handle,"time = %0d,SignImmD = %0d",$time,SignImmD);
		$fdisplay(handle,"time = %0d,ShamtD = %0d",$time,ShamtD);
		$fdisplay(handle,"time = %0d,MemtoRegD = %b",$time,MemtoRegD);
		$fdisplay(handle,"time = %0d,RegWriteD = %b",$time,RegWriteD);
		$fdisplay(handle,"time = %0d,ForwardAD = %b",$time,ForwardAD);
		$fdisplay(handle,"time = %0d,ForwardBD = %b",$time,ForwardBD);
		$fdisplay(handle,"time = %0d,PCBranchD = %0d",$time,PCBranchD);
		$fdisplay(handle,"time = %0d,PCPlus4D = %0d",$time,PCPlus4D);
		$fdisplay(handle,"time = %0d,BrEqD = %0d",$time,BrEqD);
		$fdisplay(handle,"time = %0d,BranchD = %0d",$time,BranchD);
		$fdisplay(handle,"time = %0d,PcSrc = %0d",$time,PCSrc);
		$fdisplay(handle,"time = %0d,StallF = %0d",$time,StallF);
		$fdisplay(handle,"time = %0d,StallD = %0d",$time,StallD);
		$fdisplay(handle,"time = %0d,JImmD = %0d",$time,JImmD);
		$fdisplay(handle,"time = %0d,PCJ:%0d",$time,PCJ);
		$fdisplay(handle,"time = %0d,JumpAddr = %0d",$time,JumpAddr);
	/*/
	
	/*
		$fdisplay(handle,"time = %0d,RD1E = %0d",$time,RD1E);
		$fdisplay(handle,"time = %0d,RD2E = %0d",$time,RD2E);
		$fdisplay(handle,"time = %0d,RsE = %0d",$time,RsE);
		$fdisplay(handle,"time = %0d,RtE = %0d",$time,RtE);
		$fdisplay(handle,"time = %0d,RdE = %0d",$time,RdE);
		$fdisplay(handle,"time = %0d,ALUControlE = %b",$time,ALUControlE);
		$fdisplay(handle,"time = %0d,ALUSrcE = %b",$time,ALUSrcE);
		$fdisplay(handle,"time = %0d,ALUOutE = %0d",$time,ALUOutE);
		$fdisplay(handle,"time = %0d,WriteDataE = %b",$time,WriteDataE);
		$fdisplay(handle,"time = %0d,SignImmE = %0d",$time,SignImmE);
		$fdisplay(handle,"time = %0d,ShamtE = %0d",$time,ShamtE);
		$fdisplay(handle,"time = %0d,SrcAE = %0d",$time,SrcAE);
		$fdisplay(handle,"time = %0d,SrcBE = %0d",$time,SrcBE);
		$fdisplay(handle,"time = %0d,ForwardAE = %b",$time,ForwardAE);
		$fdisplay(handle,"time = %0d,ForwardBE = %b",$time,ForwardBE);
		$fdisplay(handle,"time = %0d,MemtoRegE = %b",$time,MemtoRegE);
		$fdisplay(handle,"time = %0d,RegWriteE = %b",$time,RegWriteE);
		$fdisplay(handle,"time = %0d,WriteRegE = %0d",$time,WriteRegE);
		$fdisplay(handle,"time = %0d,RegDstE = %0d",$time,RegDstE);
	/*/
	
	
	/* for M
		$fdisplay(handle,"time = %0d,MemWriteM = %b",$time,MemWriteM);
		$fdisplay(handle,"time = %0d,WriteRegM = %0d",$time,WriteRegM);
		$fdisplay(handle,"time = %0d,RegWriteM = %b",$time,RegWriteM);
		$fdisplay(handle,"time = %0d,ALUOutM = %0d",$time,ALUOutM);
		$fdisplay(handle,"time = %0d,WriteDataM = %b",$time,WriteDataM);
		$fdisplay(handle,"time = %0d,MemtoRegM = %b",$time,MemtoRegM);
	/*/
	
	/* for WB
		$fdisplay(handle,"time = %0d,RegWriteW = %b",$time,RegWriteW);
		$fdisplay(handle,"time = %0d,WriteRegW = %0d",$time,WriteRegW);
		$fdisplay(handle,"time = %0d,ResultW = %0d",$time,ResultW);
		$fdisplay(handle,"time = %0d,MemtoRegW = %b",$time,MemtoRegW);
	/*/
	
	/* Conflict_Deal Check
		$fdisplay(handle,"time = %0d,FlushE = %b",$time,FlushE);
	/*/
	
	/*
			$display("for stall detect");
			$display("time = %0d, RtE = %d",$time,RtE);
			$display("time = %0d, RsD = %d",$time,RsD);
			$display("time = %0d, RtD = %d",$time,RtD);
			$display("time = %0d, MemtoRegE = %d",$time,MemtoRegE);
	*/
		// my clock
		#1 Clock <= ~Clock;
	end

endmodule
